package hu.arguscab.ai.util;

import hu.arguscab.ai.Edge;
import hu.arguscab.ai.QuadTree;
import hu.arguscab.game.QuadTreeRepresentation;

/**
 * 
 * @author bbodi
 *
 * @param <T> Az objektum típusa, amiből quadtreet generál (pl Térkép)
 * @param <N> Node által tárolt típus
 */
public abstract class AbstractQuadTreeCreator<T, E, N> {
	
	final private T myObj;
	private int myDepth;
	final private QuadTree<E, N> quadTree;
	final private QuadTreeRepresentation<E, N> quadRepresentation;

	protected abstract QuadNode<N> createNode(T obj, Rect r);
	protected abstract Rect createRootRect(T obj);
	protected abstract boolean isIn(N obj, QuadNode<N> node);
	
	public AbstractQuadTreeCreator(T obj) {
		myObj = obj;
		quadTree = new QuadTree<E, N>();
		quadRepresentation = new QuadTreeRepresentation<E, N>();		
	}
	
	public QuadTree<E, N> generate(int maxDepth) {
		myDepth = maxDepth;
		Rect r = createRootRect(myObj);
		
		generate(null, r, 0, 0);
		
		return quadTree;
	}

	private void generate(QuadNode<N> from, Rect r, int depth, int side) {
		// Creating a node from a specific area of the Object 
		QuadNode<N> node = createNode(myObj, r);
		if ( from != null )
			quadTree.addNode(from, node, new Edge<E>(), side);
		else
			quadTree.setRoot(node);
		if ( node.isHomogeneous() ) {
			return;
		} else if ( depth+1 <= myDepth ) {
			Rect[] childs = divide(r, depth);
			generate(node, childs[QuadTree.A], depth+1, QuadTree.A);
			generate(node, childs[QuadTree.B], depth+1, QuadTree.B);
			generate(node, childs[QuadTree.C], depth+1, QuadTree.C);
			generate(node, childs[QuadTree.D], depth+1, QuadTree.D);
		}
	}
	
	/**
	 *  1    2    3
	 *  +----+----+
	 *  | A  | B  |
	 * 2+----+----+
	 *  | C  | D  |
	 * 3+----+----+
	 * @param r
	 * @return
	 */
	private Rect[] divide(Rect r, int depth) {		
		Rect[] ret = new Rect[4];

		int w = r.getW()/2;
		int h = r.getH()/2;
		
		int x1 = r.getX();
		int x2 = x1 + w;
		
		int y1 = r.getY();
		int y2 = y1 + h;
		
		quadRepresentation.addLine(x2, y1, x2, y2+h, depth);
		quadRepresentation.addLine(x1, y2, x2+w, y2, depth);
				
		ret[QuadTree.A] = new Rect(x1, y1, w, h);
		ret[QuadTree.B] = new Rect(x2, y1, w, h);
		ret[QuadTree.C] = new Rect(x1, y2, w, h);
		ret[QuadTree.D] = new Rect(x2, y2, w, h);
		return ret;
	}
	public QuadTree<E, N> getQuadTree() {
		return quadTree;
	}
	public QuadTreeRepresentation<E, N> getQuadRepresentation() {
		return quadRepresentation;
	}
	
	public QuadNode<N> getNode(N obj) {
		QuadNode<N> node = quadTree.getRoot();
		return isInHelper(obj, node);
	}
	
	private QuadNode<N> isInHelper(N obj, QuadNode<N> node) {
		if ( isIn(obj, node) ) {
			if ( quadTree.hasChild(node) ) {
				for ( QuadNode<N> child : quadTree.childArray(node) ) {
					QuadNode<N> ret = isInHelper(obj, child);
					if ( ret != null )
						return ret; 
				}
			} else
				return node;
		}
		return null;
	}

	
	
}